#include <vex/core/handle_policy.h>
#include "CppUnitTest.h"
#include <functional>
#include <type_traits>

namespace tests {

    namespace vc = vex::core;

    struct handle_policy {
        template<class THandle>
        class has_traits {
            template<class T> 
            static char test(typename T::traits* = 0);

            template<class T> 
            static double test(T*);
        public:
            static const bool value = sizeof(char) == sizeof(test<typename std::decay<THandle>::type>(nullptr));
        };

        template<class THandle, class UHandle>
        static void correctly_implements_bool_convertible(THandle const & p_l, UHandle const & p_r)
        {
            Assert::IsTrue((!p_l == !p_r) == (!p_l.get() == !p_r.get()));
            Assert::IsTrue((!!p_l == !!p_r) == (!!p_l.get() == !!p_r.get()));
        }

        template<class THandle>
        static void correctly_implements_hashable(THandle const & p_h)
        {
            Assert::AreEqual(
                p_h.get_hash_function()(p_h),
                std::hash<typename THandle::traits::item_type>()(p_h.get()));
        }

        template<class THandle>
        static void correctly_implements_arrow_reachable(THandle const & p_h)
        {
            typedef typename THandle::traits::item_type item_t;
            typedef decltype(p_h.operator->()) arrow_op_result_t;
            Assert::IsTrue(std::is_same<item_t, arrow_op_result_t>::value);
            Assert::IsTrue(p_h.get() == p_h.operator->());
        }

        template<class THandle, class UHandle>
        static void correctly_implements_totally_ordered(THandle const & p_l, UHandle const & p_r)
        {
            Assert::IsTrue((p_l == p_r) == (p_l.get() == p_r.get()));
            Assert::IsTrue((p_l != p_r) == (p_l.get() != p_r.get()));
            Assert::IsTrue((p_l < p_r) == (p_l.get() < p_r.get()));
            Assert::IsTrue((p_l <= p_r) == (p_l.get() <= p_r.get()));
            Assert::IsTrue((p_l > p_r) == (p_l.get() > p_r.get()));
            Assert::IsTrue((p_l >= p_r) == (p_l.get() >= p_r.get()));
        }

        template<class THandle, class UHandle>
        static void correctly_implements_handle_policy(THandle const & p_l, UHandle const & p_r)
        {
            Assert::IsTrue(has_traits<THandle>::value);
            Assert::IsTrue(has_traits<UHandle>::value);
            handle_policy::correctly_implements_bool_convertible(p_l, p_r);
            handle_policy::correctly_implements_hashable(p_l);
            handle_policy::correctly_implements_hashable(p_r);
            handle_policy::correctly_implements_totally_ordered(p_l, p_r);
            handle_policy::correctly_implements_arrow_reachable(p_l);
            handle_policy::correctly_implements_arrow_reachable(p_r);
        }
    };
}